package xin.marcher.module.common.tair;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.tair.tairhash.TairHash;
import com.aliyun.tair.tairhash.params.ExhsetParams;
import com.aliyun.tair.tairroaring.TairRoaring;
import com.aliyun.tair.tairstring.TairString;
import com.aliyun.tair.tairstring.params.ExincrbyParams;
import com.aliyun.tair.tairstring.params.ExsetParams;
import com.aliyun.tair.tairstring.results.ExgetResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * </p>
 */
@Component
@Slf4j
public class TairCache {

    private JedisPool jedisPool;

    public TairCache(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public Jedis getJedis() {
        return jedisPool.getResource();
    }

    public TairString createTairString(Jedis jedis) {
        return new TairString(jedis);
    }

    private TairHash createTairHash(Jedis jedis) {
        return new TairHash(jedis);
    }

    private TairRoaring createTairRoaring(Jedis jedis) {
        return new TairRoaring(jedis);
    }


    /**
     * 指定偏移量（offset）的bit值（1或者0）
     *
     * @param key
     * @param offset
     * @param value
     */
    public void trsetbit(String key, int offset, int value) {
        try (Jedis jedis = getJedis()) {
            createTairRoaring(jedis).trsetbit(key, offset, value);
        }
    }


    /**
     * 设置Roaring Bitmap中指定偏移量（offset）的bit值为1，支持传入多个值。
     *
     * @param key
     * @param offsets
     */
    public void trappendintarray(String key, long... offsets) {
        try (Jedis jedis = getJedis()) {
            createTairRoaring(jedis).trappendintarray(key, offsets);
        }
    }


    /**
     * 获取Roaring Bitmap指定区间中bit值为1的偏移量。
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List<Long> trrangeintarray(String key, int start, int end) {
        try (Jedis jedis = getJedis()) {
            return createTairRoaring(jedis).trrangeintarray(key, start, end);
        }
    }

    /**
     * 获取Roaring Bitmap中bit值为1的数量。
     *
     * @param key
     * @return
     */
    public long trbitcount(String key) {
        try (Jedis jedis = getJedis()) {
            return createTairRoaring(jedis).trbitcount(key);
        }
    }

    /**
     * 缓存存储
     *
     * @param key
     * @param value
     * @param seconds
     * @return void
     */
    public Boolean set(String key, String value, int seconds) {

        log.info("tairString set key:{}, value:{}, seconds:{}", key, value, seconds);

        try (Jedis jedis = getJedis()) {
            TairString tairString = createTairString(jedis);
            String result;
            if (seconds > 0) {
                result = tairString.exset(key, value, new ExsetParams().ex(seconds));
            } else {
                result = tairString.exset(key, value);
            }
            return "OK".equals(result);
        }
    }

    /**
     * 缓存存储
     *
     * @param key
     * @param value
     * @param seconds
     * @return void
     */
    public Boolean setNx(String key, String value, int seconds) {
        try (Jedis jedis = getJedis()) {
            TairString tairString = createTairString(jedis);
            String result;
            if (seconds > 0) {
                result = tairString.exset(key, value, new ExsetParams().nx().ex(seconds));
            } else {
                result = tairString.exset(key, value, new ExsetParams().nx());
            }
            return "OK".equals(result);
        }
    }

    /**
     * 缓存获取
     *
     * @param key
     * @return java.lang.String
     */
    public String get(String key) {
        try (Jedis jedis = getJedis()) {
            ExgetResult<String> exget = createTairString(jedis).exget(key);
            if (exget == null) {
                return null;
            }
            return exget.getValue();
        } catch (Exception e) {
            log.error("tairString get error,key{}", key, e);
        }
        return null;
    }

    /**
     * 缓存自增
     *
     * @param key
     * @return java.lang.String
     */
    public Integer incr(String key) {
        return this.incr(key, 1);
    }

    /**
     * 缓存自增
     *
     * @param key
     * @param incrNum
     * @return java.lang.String
     */
    public Integer incr(String key, Integer incrNum) {
        try (Jedis jedis = getJedis()) {
            return createTairString(jedis).exincrBy(key, incrNum, ExincrbyParams.ExincrbyParams().min(0)).intValue();
        } catch (Exception e) {
            log.error("缓存自减失败{}", e);
            // 出现自增或者自减数据溢出等异常，直接返回操作失败
            return -1;
        }
    }

    /**
     * 缓存自减
     *
     * @param key
     * @param decrNum
     * @param times
     * @return java.lang.String
     */
    public Integer retryDecr(String key, Integer decrNum, Integer times) {
        return retryIncr(key, -Math.abs(decrNum), times);
    }

    /**
     * 缓存自增
     *
     * @param key
     * @param incrNum
     * @param times
     * @return java.lang.String
     */
    public Integer retryIncr(String key, Integer incrNum, Integer times) {
        // 至少执行一次set方法，
        if (times <= 0) {
            times = 1;
        }
        for (int i = 0; i < times; i++) {
            Integer incr = this.incr(key, incrNum);
            if (incr >= 0) {
                return incr;
            }
        }
        return -1;
    }

    /**
     * 缓存自减
     *
     * @param key
     * @param decrNum
     * @return java.lang.String
     */
    public Integer decr(String key, Integer decrNum) {
        return this.incr(key, -Math.abs(decrNum));
    }

    /**
     * 删除缓存
     *
     * @param key
     * @return boolean
     */
    public Integer delete(String key) {
        try (Jedis jedis = getJedis()) {
            return jedis.del(key).intValue();
        }
    }

    /**
     * 删除缓存
     *
     * @param keyList
     * @return boolean
     */
    public Integer mdelete(List<String> keyList) {
        try (Jedis jedis = getJedis()) {
            return jedis.del(keyList.toArray(new String[keyList.size()])).intValue();
        }
    }

    /**
     * 缓存批量获取
     *
     * @param keyList
     * @return java.util.List
     */
    public List mget(List<String> keyList) {
        try (Jedis jedis = getJedis()) {
            TairString tairString = createTairString(jedis);
            return keyList.stream().map(key -> {
                ExgetResult<String> exget = tairString.exget(key);
                if (exget != null) {
                    return exget.getValue();
                }
                return null;
            }).collect(Collectors.toList());
        }
    }

    /**
     * 存储hash对象
     *
     * @param key
     * @param field
     * @param value
     * @return
     */
    public Integer exhsetNx(String key, String field, String value) {
        try (Jedis jedis = getJedis()) {
            return createTairHash(jedis).exhset(key, field, value, ExhsetParams.ExhsetParams().nx()).intValue();
        }
    }

    /**
     * 存储hash对象
     *
     * @param key
     * @param field
     * @param value
     * @return
     */
    public Integer exhset(String key, String field, String value) {
        try (Jedis jedis = getJedis()) {
            return createTairHash(jedis).exhset(key, field, value).intValue();
        }
    }


    /**
     * 获取hash对象
     *
     * @param key
     * @param field
     * @return
     */
    public String exhget(String key, String field) {
        try (Jedis jedis = getJedis()) {
            return createTairHash(jedis).exhget(key, field);
        }
    }

    /**
     * 删除hash字段
     *
     * @param key
     * @param field
     * @return
     */
    public Boolean exhdel(String key, String field) {
        try (Jedis jedis = getJedis()) {
            Long exhdel = createTairHash(jedis).exhdel(key, field);
            return exhdel == 1;
        }
    }

    /**
     * set失败时，尝试重试，最少执行一次set方法，最多执行 times 次
     *
     * @param key
     * @param value
     * @param timeout
     * @param times
     * @return
     */
    public boolean retrySet(String key, String value, int timeout, int times) {
        // 至少执行一次set方法，
        if (times <= 0) {
            times = 1;
        }
        for (int i = 0; i < times; i++) {
            boolean setFlag = this.set(key, value, timeout);
            if (setFlag) {
                return true;
            }
        }
        return false;
    }

    public void expire(String key, int timeout) {
        try (Jedis jedis = getJedis()) {
            jedis.expire(key, timeout);
        }
    }

    public void appendJsonExhset(String key, String field, String valueKey, Object value) {
        synchronized ((key + field).intern()) {
            String exhget = this.exhget(key, field);
            JSONObject json;
            if (StringUtils.isNotBlank(exhget)) {
                json = JSON.parseObject(exhget);
            } else {
                json = new JSONObject();
            }
            json.put(valueKey, value);
            this.exhset(key, field, json.toJSONString());
        }
    }

    public Object getJsonExhset(String key, String field, String valueKey) {
        String exhget = this.exhget(key, field);
        if (StringUtils.isNotBlank(exhget)) {
            JSONObject json = JSON.parseObject(exhget);
            return json.get(valueKey);
        }
        return null;
    }

    public void removeJsonExhset(String key, String field, String valueKey) {
        synchronized ((key + field).intern()) {
            String exhget = this.exhget(key, field);
            if (StringUtils.isNotBlank(exhget)) {
                JSONObject json = JSON.parseObject(exhget);
                json.remove(valueKey);
                this.exhset(key, field, json.toJSONString());
            }
        }
    }
}
